package com.example.webcrawler.service;



import com.example.webcrawler.dto.CrawlResponse;
import com.example.webcrawler.model.DataType;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.JavascriptExecutor;
import org.springframework.stereotype.Service;

import io.github.bonigarcia.wdm.WebDriverManager;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class WebCrawlerService {


    private WebDriver driver;

    @PostConstruct
    public void init() {
        // 使用相对路径或绝对路径
        System.setProperty("webdriver.chrome.driver", "src/main/resources/drivers/chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
//        options.addArguments("--headless", "--disable-gpu", "--no-sandbox", "--disable-dev-shm-usage");
        // 注释掉 headless，使用有头浏览器（调试 + 减少被识别为爬虫的概率）
        // options.addArguments("--headless");
        options.addArguments("--disable-gpu", "--no-sandbox", "--disable-dev-shm-usage");
        options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36");
        options.addArguments("window-size=1920,1080");
        options.addArguments("lang=zh-CN,zh");
        this.driver = new ChromeDriver(options);
    }

    @PreDestroy
    public void cleanup() {
        if (driver != null) {
            driver.quit();
        }
    }

    public CrawlResponse crawlAndClassify(String url, String cssSelector) {
        try {
            // 先尝试用Jsoup获取静态内容
            CrawlResponse staticResponse = crawlWithJsoup(url, cssSelector);
            if (staticResponse.isSuccess() &&
                    staticResponse.getDataType() != DataType.UNKNOWN) {
                return staticResponse;
            }

            // 如果Jsoup失败，改用Selenium
            return crawlWithSelenium(url, cssSelector);
        } catch (Exception e) {
            CrawlResponse response = new CrawlResponse();
            response.setSuccess(false);
            response.setMessage("爬取失败: " + e.getMessage());
            return response;
        }
    }

    private CrawlResponse crawlWithJsoup(String url, String cssSelector) throws IOException {
        CrawlResponse response = new CrawlResponse();

        try {
            // 获取网页文档
            Document doc = Jsoup.connect(url).get();

            // 解析数据
            Object data;
            if (cssSelector != null && !cssSelector.isEmpty()) {
                data = parseWithSelector(doc, cssSelector);
            } else {
                data = autoParse(doc);
            }

            // 判断数据类型
            DataType dataType = determineDataType(data);

            response.setDataType(dataType);
            response.setData(data);
            response.setSuccess(true);
            response.setMessage("数据爬取成功");

        } catch (IOException e) {
            response.setSuccess(false);
            response.setMessage("爬取网页时出错: " + e.getMessage());
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMessage("处理数据时出错: " + e.getMessage());
        }

        return response;
    }

    private Object parseWithSelector(Document doc, String cssSelector) {
        Elements elements = doc.select(cssSelector);

        // 如果选择器匹配多个元素，返回列表
        if (elements.size() > 1) {
            List<Map<String, String>> list = new ArrayList<>();
            for (Element element : elements) {
                list.add(extractElementData(element));
            }
            return list;
        }
        // 如果只匹配一个元素，返回对象
        else if (elements.size() == 1) {
            return extractElementData(elements.first());
        }

        return null;
    }

    private Object autoParse(Document doc) {
        // 尝试解析为列表
        Elements listItems = doc.select("ul > li, ol > li, .list-item, [data-list-item], table tr");
        if (!listItems.isEmpty()) {
            List<Map<String, String>> listData = new ArrayList<>();
            for (Element item : listItems) {
                listData.add(extractElementData(item));
            }
            return listData;
        }

        // 尝试解析为对象
        Elements objectContainers = doc.select(".object-container, [data-object], .card, .item, .product");
        if (!objectContainers.isEmpty()) {
            if (objectContainers.size() == 1) {
                return extractElementData(objectContainers.first());
            } else {
                List<Map<String, String>> listData = new ArrayList<>();
                for (Element container : objectContainers) {
                    listData.add(extractElementData(container));
                }
                return listData;
            }
        }

        // 默认返回整个body文本
        return doc.body().text();
    }

    private Map<String, String> extractElementData(Element element) {
        Map<String, String> data = new HashMap<>();
        data.put("text", element.text());
        data.put("html", element.html());

        // 提取所有属性
        element.attributes().forEach(attr -> {
            data.put("attr_" + attr.getKey(), attr.getValue());
        });

        return data;
    }

    private DataType determineDataType(Object data) {
        if (data instanceof List) {
            return DataType.LIST;
        } else if (data instanceof Map) {
            return DataType.OBJECT;
        }
        return DataType.UNKNOWN;
    }

    private CrawlResponse crawlWithSelenium(String url, String cssSelector) {
        try {
            driver.get(url);
            // 模拟下拉滚动以加载更多内容（适用于无限滚动页面）
            long lastHeight = (long) ((JavascriptExecutor) driver).executeScript("return document.body.scrollHeight");
            for (int i = 0; i < 5; i++) { // 滚动次数可调
                ((JavascriptExecutor) driver).executeScript("window.scrollTo(0, document.body.scrollHeight);");
                Thread.sleep(2000); // 等待新内容加载
                long newHeight = (long) ((JavascriptExecutor) driver).executeScript("return document.body.scrollHeight");
                if (newHeight == lastHeight) break; // 没有新内容就停止
                lastHeight = newHeight;
            }
            driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
            Thread.sleep(3000); // 简单等待，后续可使用显式等待
            driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

            List<Map<String, String>> dataList = new ArrayList<>();

            if (cssSelector != null && !cssSelector.isEmpty()) {
                List<WebElement> elements = driver.findElements(By.cssSelector(cssSelector));
                for (WebElement element : elements) {
                    dataList.add(extractSeleniumElementData(element));
                }
            } else {
                // 默认获取整个body
                WebElement body = driver.findElement(By.tagName("body"));
                dataList.add(extractSeleniumElementData(body));
            }

            DataType dataType = dataList.size() > 1 ? DataType.LIST : DataType.OBJECT;

            CrawlResponse response = new CrawlResponse();
            response.setDataType(dataType);
            response.setData(dataList.size() == 1 ? dataList.get(0) : dataList);
            response.setMessage("数据爬取成功(Selenium)");
            response.setSuccess(true);
            return response;
        } catch (Exception e) {
            throw new RuntimeException("Selenium爬取失败: " + e.getMessage(), e);
        }
    }

    private Map<String, String> extractSeleniumElementData(WebElement element) {
        Map<String, String> data = new HashMap<>();
        data.put("text", element.getText());
        data.put("html", element.getAttribute("innerHTML"));
        data.put("outerHTML", element.getAttribute("outerHTML"));
        data.put("id", element.getAttribute("id"));
        data.put("class", element.getAttribute("class"));
        // 可以添加更多属性...
        return data;
    }
}